package l1j.server.server.model;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import l1j.opqlo.Server.L1World.L1World;
import l1j.server.Config;
import l1j.server.server.IdFactory;
import l1j.server.server.datatables.FurnitureSpawnTable;
import l1j.server.server.datatables.ItemTable;
import l1j.server.server.datatables.LetterTable;
import l1j.server.server.datatables.RaceTicketTable;
import l1j.server.server.model.Instance.L1FurnitureInstance;
import l1j.server.server.model.Instance.L1ItemInstance;
import l1j.server.server.model.Instance.L1PcInstance;
import l1j.server.server.model.item.L1ItemId;
import l1j.server.server.templates.L1Item;
import l1j.server.server.templates.L1RaceTicket;
import l1j.server.server.utils.Random;

public class L1Inventory extends L1Object {

	public class DataComparator<T> implements Comparator<L1ItemInstance> {
		@Override
		public int compare(L1ItemInstance item1, L1ItemInstance item2) {
			return item1.getEnchantLevel() - item2.getEnchantLevel();
		}
	}
	private static final long serialVersionUID = 1L;

	private static final Logger _log = Logger.getLogger(L1Inventory.class.getName());

	protected List<L1ItemInstance> _items = new CopyOnWriteArrayList<L1ItemInstance>();

	public static final int MAX_AMOUNT = 2000000000; // 2G
	private static final int CHECK_AMOUNT = 1900000001; // 1.9G +1


	public static final int MAX_WEIGHT = 1500;

	// 引数のアイテムを追加しても容量と重量が大丈夫か確認
	public static final int OK = 0;

	public static final int SIZE_OVER = 1;

	public static final int WEIGHT_OVER = 2;

	public static final int AMOUNT_OVER = 3;

	// 引数のアイテムを追加しても倉庫の容量が大丈夫か確認
	public static final int WAREHOUSE_TYPE_PERSONAL = 0;

	public static final int WAREHOUSE_TYPE_CLAN = 1;

	public int checkAddItem(final L1ItemInstance item, final long l) {
		if (item == null) {
			return -1;
		}
		if (item.getCount() <= 0 || l <= 0) {
			return -1;
		}
		if (this.getSize() > Config.MAX_NPC_ITEM
				|| this.getSize() == Config.MAX_NPC_ITEM && (!item.isStackable() || !this.checkItem(item.getItem().getItemId()))) {// 容量確認
			return SIZE_OVER;
		}

		final int weight = (int) (this.getWeight() + item.getItem().getWeight() * l / 1000+ 1);
		if (weight < 0 || item.getItem().getWeight() * l / 1000 < 0) {
			return WEIGHT_OVER;
		}
		if (weight > MAX_WEIGHT * Config.RATE_WEIGHT_LIMIT_PET) { // その他の重量確認（主にサモンとペット）
			return WEIGHT_OVER;
		}

		final L1ItemInstance itemExist = this.findItemId(item.getItemId());
		if (itemExist != null && itemExist.getCount() + l > MAX_AMOUNT) {
			return AMOUNT_OVER;
		}

		return OK;
	}

	public int checkAddItemToWarehouse(final L1ItemInstance item, final int count, final int type) {
		if (item == null) {
			return -1;
		}
		if (item.getCount() <= 0 || count <= 0) {
			return -1;
		}

		int maxSize = 100;
		if (type == WAREHOUSE_TYPE_PERSONAL) {
			maxSize = Config.MAX_PERSONAL_WAREHOUSE_ITEM;
		} else if (type == WAREHOUSE_TYPE_CLAN) {
			maxSize = Config.MAX_CLAN_WAREHOUSE_ITEM;
		}
		if (this.getSize() > maxSize
				|| this.getSize() == maxSize && (!item.isStackable() || !this.checkItem(item
						.getItem().getItemId()))) { // 容量確認
			return SIZE_OVER;
		}

		return OK;
	}

	// 強化された特定のアイテムを指定された個数以上所持しているか確認
	// 装備中のアイテムは所持していないと判別する
	public boolean checkEnchantItem(final int id, final int enchant, final int count) {
		int num = 0;
		for (final L1ItemInstance item : this._items) {
			if (item.isEquipped()) { // 装備しているものは該当しない
				continue;
			}
			if (item.getItemId() == id && item.getEnchantLevel() == enchant) {
				num++;
				if (num == count) {
					return true;
				}
			}
		}
		return false;
	}

	// 特定のアイテムを指定された個数以上所持しているか確認（矢とか魔石の確認）
	public boolean checkItem(final int id) {
		return this.checkItem(id, 1);
	}

	public boolean checkItem(final int id, final long count) {
		if (count == 0) {
			return true;
		}
		if (ItemTable.getInstance().getTemplate(id).isStackable()) {
			final L1ItemInstance item = this.findItemId(id);
			if (item != null && item.getCount() >= count) {
				return true;
			}
		} else {
			final Object[] itemList = this.findItemsId(id);
			if (itemList.length >= count) {
				return true;
			}
		}
		return false;
	}

	// 特定のアイテムを全て必要な個数所持しているか確認（イベントとかで複数のアイテムを所持しているか確認するため）
	public boolean checkItem(final int[] ids) {
		final int len = ids.length;
		final int[] counts = new int[len];
		for (int i = 0; i < len; i++) {
			counts[i] = 1;
		}
		return this.checkItem(ids, counts);
	}

	public boolean checkItem(final int[] ids, final int[] counts) {
		for (int i = 0; i < ids.length; i++) {
			if (!this.checkItem(ids[i], counts[i])) {
				return false;
			}
		}
		return true;
	}

	// 特定のアイテムを指定された個数以上所持しているか確認
	// 装備中のアイテムは所持していないと判別する
	public boolean checkItemNotEquipped(final int id, final int count) {
		if (count == 0) {
			return true;
		}
		return count <= this.countItems(id);
	}

	// インベントリ内の全てのアイテムを消す（所有者を消すときなど）
	public void clearItems() {
		L1ItemInstance item ;
		for (final Object itemObject : this._items) {
			item = (L1ItemInstance) itemObject;
			//opqlo 副本道具登入掉落地上
			if(this instanceof L1PcInventory){
				if(item.getItem().getType() ==20){
					final L1PcInventory inv = (L1PcInventory)this;
					final L1PcInstance pc = inv.getOwner();
					pc.getInventory().tradeItem(item,item.getCount(),L1World.getInstance().getInventory(pc.getX(), pc.getY(), pc.getMapId()));
					continue;
				}
			}
			//opqlo 副本道具登入掉落地上
			L1World.getInstance().removeObject(item);
		}
		this._items.clear();
	}

	// 強化された特定のアイテムを消費する
	// 装備中のアイテムは所持していないと判別する
	public boolean consumeEnchantItem(final int id, final int enchant, final int count) {
		for (final L1ItemInstance item : this._items) {
			if (item.isEquipped()) { // 装備しているものは該当しない
				continue;
			}
			if (item.getItemId() == id && item.getEnchantLevel() == enchant) {
				this.removeItem(item);
				return true;
			}
		}
		return false;
	}

	/**
	 * インベントリから指定されたアイテムIDのアイテムを削除する。L1ItemInstanceへの参照
	 * がある場合はremoveItemの方を使用するのがよい。 （こちらは矢とか魔石とか特定のアイテムを消費させるときに使う）
	 *
	 * @param itemid
	 *            - 削除するアイテムのitemid(objidではない)
	 * @param l
	 *            - 削除する個数
	 * @return 実際に削除された場合はtrueを返す。
	 */
	public boolean consumeItem(final int itemid, final long l) {
		if (l <= 0) {
			return false;
		}
		if (ItemTable.getInstance().getTemplate(itemid).isStackable()) {
			final L1ItemInstance item = this.findItemId(itemid);
			if (item != null && item.getCount() >= l) {
				this.removeItem(item, l);
				return true;
			}
		} else {
			final L1ItemInstance[] itemList = this.findItemsId(itemid);
			if (itemList.length == l) {
				for (int i = 0; i < l; i++) {
					this.removeItem(itemList[i], 1);
				}
				return true;
			} else if (itemList.length > l) { // 指定個数より多く所持している場合
				final DataComparator<L1ItemInstance> dc = new DataComparator<L1ItemInstance>();
				Arrays.sort(itemList, dc); // エンチャント順にソートし、エンチャント数の少ないものから消費させる
				for (int i = 0; i < l; i++) {
					this.removeItem(itemList[i], 1);
				}
				return true;
			}
		}
		return false;
	}
	/**
	 * このインベントリ内にある、指定されたIDのアイテムの数を数える。
	 *
	 * @return
	 */
	public long countItems(final int id) {
		if (ItemTable.getInstance().getTemplate(id).isStackable()) {
			final L1ItemInstance item = this.findItemId(id);
			if (item != null) {
				return item.getCount();
			}
		} else {
			final Object[] itemList = this.findItemsIdNotEquipped(id);
			return itemList.length;
		}
		return 0;
	}
	// _itemsから指定オブジェクトを削除(L1PcInstance、L1DwarfInstance、L1GroundInstanceでこの部分をオーバライドする)
	public void deleteItem(final L1ItemInstance item) {
		this._items.remove(item);
	}

	// アイテムＩＤから検索
	/**搜尋道具FOR ItemId*/
	public L1ItemInstance findItemId(final int id) {
		for (final L1ItemInstance item : this._items) {
			if (item.getItem().getItemId() == id) {
				return item;
			}
		}
		return null;
	}

	/**搜尋道具並回傳數量FOR ItemId*/
	public long findItemIdAndGetCount(final int id) {
		for (final L1ItemInstance item : this._items) {
			if (item.getItem().getItemId() == id) {
				return item.getCount();
			}
		}
		return 0;
	}

	/**
	 * スタック可能なアイテムリストからnameIdと同じ値を持つitemを返す
	 *
	 * @param nameId
	 * @return item null 如果沒有找到。
	 */
	public L1ItemInstance findItemNameId(String nameId) {
		for (final L1ItemInstance item : this._items) {
			if (nameId.equals(item.getItem().getIdentifiedNameId())) {
				return item;
			}
		}
		return null;
	}

	/**搜尋道具陣列FOR ItemId*/
	public L1ItemInstance[] findItemsId(final int id) {
		final ArrayList<L1ItemInstance> itemList = new ArrayList<L1ItemInstance>();
		for (final L1ItemInstance item : this._items) {
			if (item.getItemId() == id) {
				itemList.add(item);
			}
		}
		return itemList.toArray(new L1ItemInstance[] {});
	}

	/**搜尋未裝備道具陣列FOR ItemId*/
	public L1ItemInstance[] findItemsIdNotEquipped(final int id) {
		final ArrayList<L1ItemInstance> itemList = new ArrayList<L1ItemInstance>();
		for (final L1ItemInstance item : this._items) {
			if (item.getItemId() == id) {
				if (!item.isEquipped()) {
					itemList.add(item);
				}
			}
		}
		return itemList.toArray(new L1ItemInstance[] {});
	}

	// オブジェクトＩＤから検索
	/**搜尋道具FOR 流水號*/
	public L1ItemInstance getItem(final int objectId) {
		for (final Object itemObject : this._items) {
			final L1ItemInstance item = (L1ItemInstance) itemObject;
			if (item.getId() == objectId) {
				return item;
			}
		}
		return null;
	}

	// インベントリ内の全てのアイテム
	public List<L1ItemInstance> getItems() {
		return this._items;
	}

	// インベントリ内のアイテムの総数
	public int getSize() {
		return this._items.size();
	}
	// インベントリ内の総重量
	public int getWeight() {
		int weight = 0;

		for (final L1ItemInstance item : this._items) {
			weight += item.getWeight();
		}

		return weight;
	}
	public void insertItem(final L1ItemInstance item) {
	}
	public boolean isEmpty(){
		return 	this._items.isEmpty();
	}

	// オーバーライド用
	public void loadItems() {
	}

	/*
	 * アイテムを損傷・損耗させる（武器・防具も含む） アイテムの場合、損耗なのでマイナスするが 武器・防具は損傷度を表すのでプラスにする。
	 */
	public L1ItemInstance receiveDamage(final int objectId) {
		final L1ItemInstance item = this.getItem(objectId);
		return this.receiveDamage(item);
	}

	public L1ItemInstance receiveDamage(final L1ItemInstance item) {
		return this.receiveDamage(item, 1);
	}

	public L1ItemInstance receiveDamage(final L1ItemInstance item, final int count) {

		if (item != null) {
			final int itemType = item.getItem().getType2();
			final int currentDurability = item.get_durability();
			if (currentDurability == 0 && itemType == 0 || currentDurability < 0) {
				item.set_durability(0);
				return null;
			}

			// 武器・防具のみ損傷度をプラス
			if (itemType == 0) {
				final int minDurability = (item.getEnchantLevel() + 5) * -1;
				int durability = currentDurability - count;
				if (durability < minDurability) {
					durability = minDurability;
				}
				if (currentDurability > durability) {
					item.set_durability(durability);
				}
			} else {
				final int maxDurability = item.getEnchantLevel() + 5;
				int durability = currentDurability + count;
				if (durability > maxDurability) {
					durability = maxDurability;
				}
				if (currentDurability < durability) {
					item.set_durability(durability);
				}
			}

			this.updateItem(item, L1PcInventory.COL_DURABILITY);
		}
		return item;
	}

	public L1ItemInstance recoveryDamage(final L1ItemInstance item) {

		if (item != null) {

			final int itemType = item.getItem().getType2();
			final int durability = item.get_durability();
			if (durability == 0 && itemType != 0 || durability < 0) {
				item.set_durability(0);
				return null;
			}

			if (itemType == 0) {
				// 耐久度をプラスしている。
				item.set_durability(durability + 1);
			} else {
				// 損傷度をマイナスしている。
				item.set_durability(durability - 1);
			}

			this.updateItem(item, L1PcInventory.COL_DURABILITY);
		}
		return item;
	}

	// 指定したアイテムから指定個数を削除（使ったりゴミ箱に捨てられたとき）戻り値：実際に削除した数
	/**使用 道具【流水號】刪除道具數量*/
	public long removeItem(final int objectId, final long count) {
		final L1ItemInstance item = this.getItem(objectId);
		return this.removeItem(item, count);
	}

	/**使用 道具【物件】刪除整個道具*/
	public long removeItem(final L1ItemInstance item) {
		return this.removeItem(item, item.getCount());
	}

	/**使用 道具【物件】刪除道具數量*/
	public long removeItem(final L1ItemInstance item, long count) {
		if (item == null) {
			return 0;
		}
		if (item.getCount() <= 0 || count <= 0) {
			return 0;
		}
		if (item.getCount() < count) {
			count = item.getCount();
		}
		if (item.getCount() == count) {
			final int itemId = item.getItem().getItemId();
			if (itemId >= 49016 && itemId <= 49025) { // 便箋
				final LetterTable lettertable = new LetterTable();
				lettertable.deleteLetter(item.getId());
			} else if (itemId >= 41383 && itemId <= 41400) { // 家具
				for (final L1Object l1object : L1World.getInstance().getObject()) {
					if (l1object instanceof L1FurnitureInstance) {
						final L1FurnitureInstance furniture = (L1FurnitureInstance) l1object;
						if (furniture.getItemObjId() == item.getId()) { // 既に引き出している家具
							FurnitureSpawnTable.getInstance().deleteFurniture(furniture);
						}
					}
				}
			} else if (item.getItemId() == 40309) {// Race Tickets
				RaceTicketTable.getInstance().deleteTicket(item.getId());
			}
			this.deleteItem(item);
			L1World.getInstance().removeObject(item);
		} else {
			item.setCount(item.getCount() - count);
			this.updateItem(item);
		}
		return count;
	}

	/**使用 道具【編號】刪除道具數量*/
	public long removeItemByItemId(final int itemid , final long count) {
		for (final L1ItemInstance item : this._items) {
			if (item.getItem().getItemId() == itemid) {
				return this.removeItem(item, count);
			}
		}
		return 0;
	}

	public void shuffle() {
		Collections.shuffle(this._items);
	}

	/**
	 * 全新物件加入背包
	 * @param id
	 * @param count
	 * @return
	 */
	public synchronized L1ItemInstance storeItem(final int id, final long l) {
		try {
			if (l <= 0) {
				return null;
			}
			final L1Item temp = ItemTable.getInstance().getTemplate(id);
			if (temp == null) {
				return null;
			}

			if (temp.isStackable()) {
				final L1ItemInstance item = new L1ItemInstance(temp, l);

				if (this.findItemId(id) == null) { // 新しく生成する必要がある場合のみIDの発行とL1Worldへの登録を行う
					item.setId(IdFactory.getInstance().nextId());
					L1World.getInstance().storeObject(item);
				}

				return this.storeItem(item);
			}

			// スタックできないアイテムの場合
			L1ItemInstance result = null;
			for (int i = 0; i < l; i++) {
				final L1ItemInstance item = new L1ItemInstance(temp, 1);
				item.setId(IdFactory.getInstance().nextId());
				L1World.getInstance().storeObject(item);
				this.storeItem(item);
				result = item;
			}
			// 最後に作ったアイテムを返す。配列を戻すようにメソッド定義を変更したほうが良いかもしれない。
			return result;
		} catch (final Exception e) {
			_log.log(Level.SEVERE ,e.getLocalizedMessage(), e);
		}
		return null;
	}

	// DROP、購入、GMコマンドで入手した新しいアイテムの格納
	/**
	 * 背包中新物品的增加
	 * (物品購買/道具交換)
	 * @param item
	 * @return
	 */
	public synchronized L1ItemInstance storeItem(L1ItemInstance item) {
		try {
			if (item.getCount() <= 0) {
				return null;
			}
			final L1Item Item = item.getItem();
			final int itemId = Item.getItemId();
			if (item.isStackable()) {
				L1ItemInstance findItem;
				if (itemId == 40309) { // Race Tickets
					findItem = this.findItemNameId(Item.getIdentifiedNameId());
					/**自動兌換支票 opqlo*/
				} else if (itemId == L1ItemId.ADENA){
					findItem = this.findItemId(itemId);
					if(findItem !=null ){
						final long allcount = findItem.getCount() + item.getCount();
						if(allcount>=CHECK_AMOUNT){
							findItem.setCount(allcount);
							final int tt = (int)allcount / 100000000;
							this.removeItem(findItem, (tt*100000000));
							final L1ItemInstance newitem = this.findItemId(60308);
							item = ItemTable.getInstance().createItem(60308);
							item.setCount(tt);
							findItem = newitem;
						}
					}
				} else {
					findItem = this.findItemId(itemId);
				}
				if (findItem != null) {
					findItem.setCount(findItem.getCount() + item.getCount());
					this.updateItem(findItem);
					return findItem;
				}
			}
			if (itemId == 40309) {// Race Tickets
				String[] temp = Item.getIdentifiedNameId().split(" ");
				temp=	temp[temp.length-1].split("-");
				final L1RaceTicket ticket = new L1RaceTicket();
				ticket.set_itemobjid(item.getId());
				ticket.set_round(Integer.parseInt(temp[0]));
				ticket.set_allotment_percentage(0.0);
				ticket.set_victory(0);
				ticket.set_runner_num(Integer.parseInt(temp[1]));
				ticket.set_runner_name(Item.getTicketName());
				RaceTicketTable.getInstance().storeNewTiket(ticket);
			}

			item.setX(this.getX());
			item.setY(this.getY());
			item.setMap(this.getMapId());
			int chargeCount = Item.getMaxChargeCount();
			if (itemId == 40006 || itemId == 40007 || itemId == 40008
					|| itemId == 140006 || itemId == 140008 || itemId == 41401) {

				chargeCount -= Random.nextInt(5);
			}
			if (itemId == 20383) {
				chargeCount = 50;
			}
			item.setChargeCount(chargeCount);
			if (Item.getType2() == 0 && Item.getType() == 2) { // light系アイテム
				item.setRemainingTime(Item.getLightFuel());
			} else if(item.getRemainingTime() == 0){
				item.setRemainingTime(Item.getMaxUseTime());
			}else{
				item.setRemainingTime(item.getRemainingTime());
			}
			if(item.getBless() ==1){
				item.setBless(Item.getBless());
			}else{
				item.setBless(item.getBless());
			}
			// 道具天數刪除系統
			if (Item.getDelete_Day() > 0) { // ● 指定天數
				final int delday = Item.getDelete_Day();
				final Timestamp deleteDay = new Timestamp(
						System.currentTimeMillis()
						+ 86400000 * delday);
				item.setDeleteDate(deleteDay);
			} else if (Item.getDelete_Date() != null) { // ● 指定日期
				item.setDeleteDate(Item.getDelete_Date());
			}
			// end
			this._items.add(item);
			this.insertItem(item);
			return item;
		} catch (final Exception e) {
			_log.log(Level.SEVERE ,e.getLocalizedMessage(), e);
		}
		return null;
	}

	// /trade、倉庫から入手したアイテムの格納
	public synchronized L1ItemInstance storeTradeItem(L1ItemInstance item) {
		try {
			if (item == null) {
				return null;
			}
			if (item.getCount() <= 0) {
				return null;
			}

			if (item.isStackable()) {
				final int itemId = 	item.getItemId();
				L1ItemInstance findItem = this.findItemId(itemId);
				/***自動兌換支票opqlo**/
				if (itemId == L1ItemId.ADENA){
					findItem = this.findItemId(itemId);
					if(findItem !=null ){
						final long allcount = findItem.getCount() + item.getCount();
						if(allcount >=CHECK_AMOUNT){
							findItem.setCount(allcount);
							int tt = (int)allcount / 100000000;
							this.removeItem(findItem, (tt*100000000));
							final L1ItemInstance newitem = this.findItemId(60308);
							findItem = newitem;
							item = ItemTable.getInstance().createItem(60308);
							item.setCount(tt);
						}
					}
				}
				if (findItem != null) {
					findItem.setCount(findItem.getCount() + item.getCount());
					this.updateItem(findItem);
					return findItem;
				}
			}
			item.setX(this.getX());
			item.setY(this.getY());
			item.setMap(this.getMapId());
			this._items.add(item);
			this.insertItem(item);
			return item;
		} catch (final Exception e) {
			_log.log(Level.SEVERE ,e.getLocalizedMessage(), e);
		}
		return null;
	}

	// 引数のインベントリにアイテムを移譲
	public synchronized L1ItemInstance tradeItem(final int objectId, final int count,final L1Inventory inventory) {
		final L1ItemInstance item = this.getItem(objectId);
		return this.tradeItem(item, count, inventory);
	}

	public synchronized L1ItemInstance tradeItem(final L1ItemInstance item,long count,final L1Inventory inventory) {
		//opqlo 防洗
		if (item == null||item.isEquipped()) {
			return null;
		}
		count = Math.abs(count);
		count = Math.min(count,2000000000);
		count = (int) Math.min(count,item.getCount());
		if(count > 0 && count < 2000000000 && item.getCount() > 0
				&& item.getCount() >= count){

			if (item.getCount() <= 0 || count <= 0) {
				return null;
			}
			if (!this.checkItem(item.getItem().getItemId(), count)) {
				return null;
			}
			/**opqlo 防止破20E**/
			/*if(checkAddItem(item , count) != OK){
			return null;
		}
			 */
			L1ItemInstance carryItem;

			if (item.getCount() <= count) {
				this.deleteItem(item);
				carryItem = item;
			} else {
				item.setCount(item.getCount() - count);
				this.updateItem(item);
				carryItem = ItemTable.getInstance().createItem(item.getItem().getItemId());
				carryItem.setCount(count);
				carryItem.setEnchantLevel(item.getEnchantLevel());
				carryItem.setIdentified(item.isIdentified());
				carryItem.set_durability(item.get_durability());
				carryItem.setChargeCount(item.getChargeCount());
				carryItem.setRemainingTime(item.getRemainingTime());
				carryItem.setLastUsed(item.getLastUsed());
				carryItem.setBless(item.getBless());
			}
			return inventory.storeTradeItem(carryItem);
			//opqlo 防洗
		}
		return null;

		//opqlo 防洗
	}
	public void updateItem(final L1ItemInstance item) {
	}

	public void updateItem(final L1ItemInstance item, final int colmn) {
	}

}
